package database;

//package au.com.bytecode.opencsv;
/**
Copyright 2005 Bytecode Pty Ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
 */
import java.io.IOException;
import java.io.Reader;
import java.math.BigDecimal;
import java.sql.Clob;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;

/**
 * Helper class for processing JDBC ResultSet objects.
 * 
 * @author Tristan Daniel Boucher (Formatting)
 * @version Commit #24 
 */
public class ResultSetHelperService implements ResultSetHelper 
{
  /**
   * The Clob objects buffer size constant.
   */
  public static final int CLOBBUFFERSIZE = 2048;

  // note: we want to maintain compatibility with Java 5 VM's
  // These types don't exist in Java 5
  private static final int NVARCHAR = -9;
  private static final int NCHAR = -15; 
  private static final int LONGNVARCHAR = -16;
  private static final int NCLOB = 2011;

  /**
   * Constructs a String array from all the column names.
   * 
   * @param the_rs The result set to work with.
   * @return The column names as a string array.
   * @throws SQLException The Exception.
   */
  public String[] getColumnNames(final ResultSet the_rs) throws SQLException 
  {
    final List<String> names = new ArrayList<String>();
    final ResultSetMetaData metadata = the_rs.getMetaData();

    for (int i = 0; i < metadata.getColumnCount(); i++) 
    {
      names.add(metadata.getColumnName(i + 1));
    }

    final String[] name_array = new String[names.size()];
    return names.toArray(name_array);
  }

  /**
   * Constructs a String array from all the values of a column.
   * 
   * @param the_rs The result set.
   * @return The values of a column.
   * @throws SQLException The exceptions thrown.
   * @throws IOException The exceptions thrown.
   */
  public String[] getColumnValues(final ResultSet the_rs) throws SQLException, IOException 
  {

    final List<String> values = new ArrayList<String>();
    final ResultSetMetaData metadata = the_rs.getMetaData();

    for (int i = 0; i < metadata.getColumnCount(); i++) 
    {
      values.add(getColumnValue(the_rs, metadata.getColumnType(i + 1), i + 1));
    }

    final String[] value_array = new String[values.size()];
    return values.toArray(value_array);
  }

  /**
   * Retrieves the value of an object as a string.
   * @param obj The object in question.
   * @return Where were you on the night of the 23rd??? Bwahahaha wouldn't you like to know?
   */
  private String handleObject(final Object obj)
  {
    return obj == null ? "" : String.valueOf(obj);
  }

  /**
   * Retrieves the value of an object as a string.
   * @param the_decimal The Decimal object being passed.
   * @return The string representation of this BigDecimal object.
   */
  private String handleBigDecimal(final BigDecimal the_decimal) 
  {
    return the_decimal == null ? "" : the_decimal.toString();
  }

  /**
   * Retrieves the value of an object as a string.
   * @param the_rs The result set.
   * @param the_column_index The index of the column this came from.
   * @return The representation of the long in the result set.
   * @throws SQLException The exception thrown.
   */
  private String handleLong(final ResultSet the_rs, 
                            final int the_column_index) throws SQLException 
  {
    final long lv = the_rs.getLong(the_column_index);
    return the_rs.wasNull() ? "" : Long.toString(lv);
  }

  /**
   * Retrieves the value of an object as a string.
   * @param the_rs The result set.
   * @param the_column_index The index of the column this came from.
   * @return The representation of the integer in the result set.
   * @throws SQLException The exception thrown.
   */
  private String handleInteger(final ResultSet the_rs, 
                               final int the_column_index) throws SQLException 
  {
    final int i = the_rs.getInt(the_column_index);
    return the_rs.wasNull() ? "" : Integer.toString(i);
  }
  
  /**
   * Retrieves the value of an object as a string.
   * @param the_rs The result set.
   * @param the_column_index The Index of the column this came from.
   * @return The representation of the date of the timeset at the column index.
   * @throws SQLException The exception thrown.
   */
  private String handleDate(final ResultSet the_rs, 
                            final int the_column_index) throws SQLException 
  {
    final java.sql.Date date = the_rs.getDate(the_column_index);
    String value = null;
    if (date != null) 
    {
      final SimpleDateFormat date_format = new SimpleDateFormat("dd-MMM-yyyy");
      value =  date_format.format(date);
    }
    return value;
  }

  /**
   * Retrieves the value of an object as a string.
   * @param the_time The time passed.
   * @return The toString value of the time.
   */
  private String handleTime(final Time the_time) 
  {
    return the_time == null ? null : the_time.toString();
  }

  /**
   * Retrieves the value of an object as a string.
   * @param the_timestamp The timestamp object in question.
   * @return The string representation of this timestamp object.
   */
  private String handleTimestamp(final Timestamp the_timestamp) 
  {
    final SimpleDateFormat time_format = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss");
    return the_timestamp == null ? null : time_format.format(the_timestamp);
  }

  /**
   * Returns the value of a column.
   * @param the_rs The ResultSet.
   * @param the_col_type The column type.
   * @param the_col_index The index of the column.
   * @return The value of the column as a String.
   * @throws SQLException The exception that can be thrown.
   * @throws IOException The exception that can be thrown.
   */
  private String getColumnValue(final ResultSet the_rs, final int the_col_type, 
                                final int the_col_index)
    throws SQLException, IOException 
  {

    String value = "";

    switch (the_col_type)
    {
      case Types.BIT:
      case Types.JAVA_OBJECT:
        value = handleObject(the_rs.getObject(the_col_index));
        break;
      case Types.BOOLEAN:
        final boolean b = the_rs.getBoolean(the_col_index);
        value = Boolean.valueOf(b).toString();
        break;
      case NCLOB: // todo : use rs.getNClob
      case Types.CLOB:
        final Clob c = the_rs.getClob(the_col_index);
        if (c != null) 
        {
          value = read(c);
        }
        break;
      case Types.BIGINT:
        value = handleLong(the_rs, the_col_index);
        break;
      case Types.DECIMAL:
      case Types.DOUBLE:
      case Types.FLOAT:
      case Types.REAL:
      case Types.NUMERIC:
        value = handleBigDecimal(the_rs.getBigDecimal(the_col_index));
        break;
      case Types.INTEGER:
      case Types.TINYINT:
      case Types.SMALLINT:
        value = handleInteger(the_rs, the_col_index);
        break;
      case Types.DATE:
        value = handleDate(the_rs, the_col_index);
        break;
      case Types.TIME:
        value = handleTime(the_rs.getTime(the_col_index));
        break;
      case Types.TIMESTAMP:
        value = handleTimestamp(the_rs.getTimestamp(the_col_index));
        break;
      case NVARCHAR: // todo : use rs.getNString
      case NCHAR: // todo : use rs.getNString
      case LONGNVARCHAR: // todo : use rs.getNString
      case Types.LONGVARCHAR:
      case Types.VARCHAR:
      case Types.CHAR:
        value = the_rs.getString(the_col_index);
        break;
      default:
        value = "";
    }


    if (value == null)
    {
      value = "";
    }

    return value;

  }

  /**
   * Reads and returns a line from the csv file.
   * @param the_c The Clob.
   * @return String The line that was read.
   * 
   * @throws SQLException The exceptions that can be thrown.
   * @throws IOException The exceptions that can be thrown.
   */
  private static String read(final Clob the_c) throws SQLException, IOException
  {
    final StringBuilder sb = new StringBuilder((int) the_c.length());
    final Reader r = the_c.getCharacterStream();
    final char[] cbuf = new char[CLOBBUFFERSIZE];
    int n;
    while ((n = r.read(cbuf, 0, cbuf.length)) != -1) 
    {
      sb.append(cbuf, 0, n);
    }
    return sb.toString();
  }
}

